#include "select.h"
#include "1mysql.h"

#define SER_IP "192.168.10.47"
#define SER_PORT 6789

#define LISTEN_MAX 100
NowClient user[NOW_MAX]; // 表示当前在线人的资料
 
/********************************************
 * TCP通信服务器端流程
 * 1，创建套接字
 * 2，绑定
 * 3，监听
 * 4，等待连接
 * 5，收发数据
 * 6，关闭套接字
 * *************************************************/

int all_clifd[256] = {0}; // 存放所有的客户端描述符
int count = 0;            // 计数
MYSQL *my_sql;            // 数据库描述符

void accept_do_handler(int sockfd);
void recv_do_handler(int sockfd);
void send_to_all_cli(char *buf);

int sockfd;
void guan() // 关闭函数
{
    close(sockfd);
    mysql_close(my_sql);
    exit(-1);
}
int main(int argc, char *argv[])
{
    pid_t pid = fork();
    if (pid == 0)
    {
        // 1.创建套接字   IPV4  流式套接字  TCP协议
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
            perror("socket");
            return -1;
        }
        printf("sockfd = %d\n", sockfd);
        signal(10, guan);
        // 2.绑定
        struct sockaddr_in seraddr;
        memset(&seraddr, 0, sizeof(struct sockaddr_in));
        seraddr.sin_family = AF_INET; // ipv4协议类型
        // seraddr.sin_addr;//ip地址  网络字节序
        // inet_aton(SER_IP,&seraddr.sin_addr);
        seraddr.sin_addr.s_addr = inet_addr(SER_IP);
        seraddr.sin_port = htons(SER_PORT); // 端口    网络字节序
        int opt = 1;
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        if (bind(sockfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr_in)) == -1)
        {
            perror("bind");
            return -1;
        }
        printf("绑定成功\n");

        // 3.监听
        if (listen(sockfd, LISTEN_MAX) == -1)
        {
            perror("listen");
            return -1;
        }
        printf("监听成功\n");

        // 非阻塞轮询
        int maxfd = sockfd;
        // 处理连接请求--》sockfd发生可读变化
        // 接收数据--》clifd发生可读变化
        fd_set readfds;      // 检测可读变化
        my_sql = Sconnect(); // 连接数据库
        int ret = 0;
        // struct timeval select_time;
        int i = 0;
        while (1)
        {
            //
            // 先去清空集合
            FD_ZERO(&readfds);
            // 再去添加
            FD_SET(sockfd, &readfds);

            for (i = 0; i < count; i++)
            {
                // 添加描sockfd述符
                FD_SET(all_clifd[i], &readfds);
                // 求出来最大的
                if (all_clifd[i] > maxfd)
                {
                    maxfd = all_clifd[i];
                }
            }
            printf("\n");

            // 永久阻塞
            ret = select(maxfd + 1, &readfds, NULL, NULL, NULL);
            if (ret == -1)
            {
                perror("select");
                return -1;
            }
            else if (ret == 0)
            {
                printf("超时\n");
                continue;
            }
            // 有变化，会打断阻塞，将集合里边没有变化的删除掉
            else
            {
                // printf("有文件描述符发生变化\n");
                // 判断是谁发生的变化，根据变化去作处理
                // 轮询从0到maxfd所有的文件描述符
                for (i = 0; i <= maxfd; i++)
                {
                    // 检查发生变化的是否是readfds集合里边的
                    if (FD_ISSET(i, &readfds) == 1)
                    {
                        // 服务器套接字描述符发生变化
                        // 有客户端请求连接
                        if (i == sockfd)
                        {
                            accept_do_handler(sockfd);
                        }
                        // 有客户端发消息
                        else
                        {

                            recv_do_handler(i);
                        }
                    }
                }
            }
        }
    }
    else
    {
        char a;
        while (1)
        {
            printf("y/n\n");
            a = getchar();
            if (a == 'y')
            {
                printf("1");
                kill(pid, 10);
                exit(0);
            }
        }
    }
}

void accept_do_handler(int sockfd)
{
    // 4.等待连接
    // 存放连接成功之后，客户端的IP地址和端口
    struct sockaddr_in cliaddr;
    memset(&cliaddr, 0, sizeof(struct sockaddr_in));
    socklen_t len = sizeof(struct sockaddr_in);
    int clifd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
    if (clifd == -1)
    {
        perror("accept");
        return;
    }

    // 存储所有的客户端描述符
    all_clifd[count] = clifd;
    count++;
}

void recv_do_handler(int client_socket)
{
    // 5.数据收发

    int i = 0;
    int ret = 0;
    int pos = 0; // 记录下表
    Msg msg;
    memset(&msg, 0, sizeof(msg));

    // 阻塞
    ret = recv(client_socket, &msg, sizeof(msg), 0);
    char shi[1024];
    sprintf(shi, "%s %s %s %s", msg.fromname, msg.account, msg.signname, msg.password);
    printf("%s\n", shi);
    if (ret == -1)
    {
        perror("recv");
        return;
    }
    else if (ret == 0)
    {

        for (i = 0; i < count; i++)
        {
            if (all_clifd[i] == client_socket)
            {
                all_clifd[i] = 0;
                pos = i;
                break;
            }
        }
        for (i = pos; i < count - 1; i++)
        {
            all_clifd[i] = all_clifd[i + 1];
        }
        all_clifd[count - 1] = 0;
        count--;
    }
    else
    {

        switch (msg.cmd)
        {
        case 1: // 注册
            regiss(client_socket, &msg);
            break;
        case 2: // 登录
            ret = entry(client_socket, &msg);
            send(client_socket, &msg, sizeof(Msg), 0);
            if (ret == 1)
            {
                // 在线人数加1
                int i;
                for (i = 0; i < NOW_MAX; i++)
                {
                    if (user[i].socket == 0)
                    {
                        strcpy(user[i].name, msg.fromname);
                        user[i].socket = client_socket;
                        user[i].jin = 0;
                        user[i].ti = 0;
                        printf("客户端在线人数加一\n");
                        break;
                    }
                }
                char guanbuf[1000];
                sprintf(msg.msg, "%s已登录", msg.fromname);
                msg.cmd = 22;
                // 用户界面

                for (i = 0; i < NOW_MAX; i++)
                {
                    if (user[i].socket == 0)
                        continue;
                    if (user[i].socket == client_socket)
                        continue;
                    send(user[i].socket, &msg, sizeof(Msg), 0);
                }
            }
            break;
        case 3: // 群聊
            server_chatall(client_socket, &msg);
            server_save(&msg);
            break;
        case 4: // 私聊
            server_chatone(client_socket, &msg);
            server_save(&msg);
            break;
        case 5: // 退出登录
            server_entryout(client_socket, &msg);
            sprintf(msg.msg, "%s已下线", msg.fromname);
            msg.cmd = 22;
            for (i = 0; i < NOW_MAX; i++)
            {
                if (user[i].socket == 0)
                    continue;
                if (user[i].socket == client_socket)
                    continue;
                send(user[i].socket, &msg, sizeof(Msg), 0);
            }
            break;
        case 6: // 在线人数
            see_nowuser(client_socket, &msg);
            break;
        case 7: // 改签名
            server_revise_sign(client_socket, &msg);
            break;
        case 8: // 改密码
            server_revise_password(client_socket, &msg);
            break;
        case 9: // 传输文件
            server_transfer_file(client_socket, &msg);
            break;
        case 10: // 接受传输文件
            server_transfer_file_y(&msg);
            break;
        case -10: // 拒绝传输文件
            server_transfer_file_n(&msg);
            break;
        case 11: // 一切条件都已成立,直接开始传输
            server_start_transfer_file(&msg);
            break;
        case 87: // 禁言
            shutup(client_socket, &msg);
            break;
        case 88: // 移除
            sremove(client_socket, &msg);
            break;
        case 99: // 注销
            server_logout(client_socket, &msg);
            break;
        }
    }
}

int regiss(int client_socket, Msg *msg) // 注册
{
    // printf("nihaos%d\n",__LINE__);

    int flag;
    int i = -1, j = -1, a = 0;
    i = search(my_sql, msg);
    j = search1(my_sql);
    if (j == 0)
    {
        a = 1;
    }
    printf("%d\n", i);
    if (i == 0)
    { // printf("nihaos%d\n",__LINE__);
        flag = data_insert(my_sql, msg, client_socket, a);
        msg->cmd += 1000;
    }
    else if (i == 1)
        msg->cmd = -1;
    printf("%d\n", msg->cmd);
    // printf("nihaos%d\n",__LINE__);
    send(client_socket, msg, sizeof(Msg), 0);
}

int entry(int client_socket, Msg *msg)
{
    printf(" %s进行登录\n", msg->fromname);

    // 用户登录

    int flag = Entry_User(my_sql, msg, client_socket);
    if (flag == -1)
    {
        printf("登录失败,系统繁忙\n");
        msg->cmd = -1;
        return -1;
    }

    if (flag == -2)
    {
        printf("登录失败,用户名不存在\n");
        msg->cmd = -2;
        return -2;
    }
    if (flag == -3)
    {
        printf("登录失败,用户已登录\n");
        msg->cmd = -3;
        return -3;
    }
    if (flag == -4)
    {
        printf("登录失败,密码错误\n");
        msg->cmd = -4;
        return -4;
    }
    else
    {
        printf("登录成功\n");
        msg->cmd += 1000;
        return 1;
    }
}

void server_entryout(int client_socket, Msg *msg) // 退出登录
{
    int ret = change(my_sql, "type", "0", msg->account);
    // printf("%d\n",client_socket);
    if (ret == 0)
    {
        int i;
        for (i = 0; i < NOW_MAX; i++)
        {
            if (user[i].socket == client_socket)
            {
                user[i].socket = 0;
                printf("客户端在线人数减一\n");
                break;
            }
        }
        msg->cmd = 5;
        sprintf(msg->msg, "退出登录成功");
    }
    else
    {
        sprintf(msg->msg, "退出登录失败");
        msg->cmd = -5;
    }
    // printf("%d\n",client_socket);
    char shi[1024];
    sprintf(shi, "%s %s %s %s %d", msg->fromname, msg->account, msg->signname, msg->password, msg->cmd);
    printf("%s\n", shi);
    send(client_socket, msg, sizeof(Msg), 0);
    // printf("fasongcg\n");
}

void server_chatall(int client_socket, Msg *msg) // 群聊
{
    printf(" %s 进行群发.\n", msg->fromname);

    int j;
    for (j = 0; j < NOW_MAX; j++)
    {
        if (user[j].socket == client_socket)
        {
            if (user[j].ti == 1)
            {
                memset(msg->msg, 0, 1000);
                sprintf(msg->msg, "发送失败已被移除群聊");
                write(client_socket, msg, sizeof(Msg));
                return;
            }
            if (user[j].jin == 1)
            {
                memset(msg->msg, 0, 1000);
                sprintf(msg->msg, "发送失败已被禁言");
                write(client_socket, msg, sizeof(Msg));
                return;
            }

            break;
        }
    }
    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && user[i].socket != client_socket && user[i].ti != 1)
        {
            write(user[i].socket, msg, sizeof(Msg));
        }
    }
}

void server_chatone(int client_socket, Msg *msg) // 私聊
{
    printf("私聊 %s发送信息给%s\n", msg->fromname, msg->localname);

    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
        {
            write(user[i].socket, msg, sizeof(Msg));
            printf("私聊成功\n");
            break;
        }
    }
    if (i == NOW_MAX)
    {
        msg->cmd = -3; // 表示私聊失败
        write(client_socket, msg, sizeof(Msg));
        printf("私聊失败\n");
    }
}

void server_logout(int client_socket, Msg *msg) // 注销
{
    int ret = mysql_delete(my_sql, msg->account);
    if (ret == 0)
    {
        printf("注销成功\n");
        int i;
        for (i = 0; i < NOW_MAX; i++)
        {
            if (user[i].socket == client_socket)
            {
                user[i].socket = 0;
                printf("客户端在线人数减一\n");
                break;
            }
        }
        msg->cmd = 99;
    }
    else
        msg->cmd = -99;
    send(client_socket, msg, sizeof(Msg), 0);
}

void server_save(Msg *msg) // 保存聊天记录
{
    char buf2[120];
    char ai[1250];
    memset(buf2, 0, sizeof(buf2));
    memset(ai, 0, sizeof(ai));

    sprintf(buf2, "../serverliaotianjilu/all.txt");

    int ps = open(buf2, O_WRONLY | O_CREAT | O_APPEND, 0644);
    sprintf(ai, "%s->%s:%s\n", msg->fromname, msg->localname, msg->msg);
    write(ps, ai, strlen(ai));
    close(ps);
}

void see_nowuser(int client_socket, Msg *msg) // 在线人员
{
    printf("%s 查看当前在线人员\n", msg->fromname);

    int i;
    int len;
    char buf[1024] = {0};
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0)
        {
            strcat(buf, user[i].name);
            len = strlen(buf);
            buf[len] = ' ';
        }
    }
    strcpy(msg->msg, buf);

    write(client_socket, msg, sizeof(Msg));
    printf("查看成功\n");
}

void server_revise_sign(int client_socket, Msg *msg) // 修改个性签名
{
    printf("%s 修改个性签名\n", msg->fromname);
    int ret = change(my_sql, "signname", msg->signname, msg->account); // 修改数据库
    if (ret == -1)
    {
        msg->cmd = -7;
        printf("%s 修改个性签名失败\n", msg->fromname);
        write(client_socket, msg, sizeof(Msg));
    }
    printf("%s 修改个性签名成功\n", msg->fromname);
    write(client_socket, msg, sizeof(Msg));
}

// 修改密码
void server_revise_password(int client_socket, Msg *msg)
{
    printf("%s 修改密码\n", msg->fromname);
    int ret = change(my_sql, "passwd", msg->password, msg->account);
    if (ret == -1)
    {
        msg->cmd = -8;
        printf("%s 修改密码失败\n", msg->fromname);
        write(client_socket, msg, sizeof(Msg));
    }
    printf("%s 修改密码成功\n", msg->fromname);
    write(client_socket, msg, sizeof(Msg));
}

void shutup(int client_socket, Msg *msg) // 禁言
{
    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (strcmp(user[i].name, msg->localname) == 0)
        {

            user[i].jin = 1;

            break;
        }
    }
    write(client_socket, msg, sizeof(Msg));
}

void sremove(int client_socket, Msg *msg) // 移除
{
    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (strcmp(user[i].name, msg->localname) == 0)
        {

            user[i].ti = 1;

            break;
        }
    }
    write(client_socket, msg, sizeof(Msg));
}

// 传输文件
void server_transfer_file(int client_socket, Msg *msg)
{
    printf("%s 请求发送%s文件给 %s\n", msg->fromname, msg->signname, msg->localname);
    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
        {
            write(user[i].socket, msg, sizeof(Msg));
            printf("发送给%s信息进行判断是否接受\n", msg->localname);
            break;
        }
    }
    if (i == NOW_MAX)
    {
        msg->cmd == -9; // 表示传输文件失败
        write(client_socket, msg, sizeof(Msg));
        printf("发送文件失败,好友不在线或不存在\n");
    }
}

void server_transfer_file_y(Msg *msg) // 接收文件
{
    printf("%s 接受文件传输.\n", msg->fromname);

    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
        {
            write(user[i].socket, msg, sizeof(Msg));
            break;
        }
    }
}

// 拒绝文件
void server_transfer_file_n(Msg *msg)
{
    printf("%s 不愿意接受文件传输.\n", msg->fromname);

    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
        {
            write(user[i].socket, msg, sizeof(Msg));
            break;
        }
    }
}

void server_start_transfer_file(Msg *msg)
{
    // printf("文件传输中\n");
    int i;
    for (i = 0; i < NOW_MAX; i++)
    {
        if (user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
        {
            write(user[i].socket, msg, sizeof(Msg)); // 写文件数据
            break;
        }
    }
    if (msg->num != 1024)
    {
        printf("文件传输完成\n");
    }
}